import React from 'react';
import { useMemo, useReducer, useCallback, useRef, useEffect } from 'react';
import { Modal, Button, Upload, message, Badge, Progress } from 'antd';
import { InboxOutlined } from '@ant-design/icons';
import type { UploadChangeParam } from 'antd/lib/upload';
import type { UploadFile } from 'antd/es/upload/interface';
import DataEdit from '../base/DataEdit';
import { useModel } from 'umi';
import { format } from '@/utils/datetime';
import {
  dataImport,
  dataProgress,
  deleteUploadFile,
  downLoadTemplate,
} from '@/services/dataImport';
import _ from 'lodash';
import type { ProFormInstance } from '@ant-design/pro-form';
import { Base64 } from 'js-base64';
let timer: number = 0;
export const downLoadTemplateFile = (tempName?: string) => {
  if (_.isEmpty(tempName)) {
    message.error('没有指定模版文件的名称');
    return;
  }
  downLoadTemplate(tempName!)
    .then(function (resp) {
      const a = document.createElement('a');
      const url = window.URL.createObjectURL(resp.data);
      const filename = resp.response.headers.get('Content-Disposition');
      a.href = url;
      a.download = Base64.decode(filename!);
      document.body.appendChild(a);
      a.click();
      setTimeout(function () {
        document.body.removeChild(a);
      }, 1000);
      window.URL.revokeObjectURL(url);
    })
    .catch(function (ex) {
      console.error('下载文件', tempName, '出错', ex);
    });
};
// 属性类型
type PropField = {
  /** 导入配置名称，按照这个名称生成后台的配置表，必须唯一 */
  importName: string;
  /** 导入使用后台dao的名称 */
  daoName: string;
  /** 导入使用的控件列表 */
  controls: API.Control[];
  /** 导入方式 */
  importMode: API.ImportMode;
  /** 区分一条数据的唯一键，用于导入模式InsertAndUpdate和UpdateOnly */
  keys?: string[];
  /** 导入窗口显示的标题 */
  title: string;
  /** 导入使用的参数，填写control的name */
  paramNames?: string[];
  /** 导入使用的参数值， 比如当前用户{uid:'15'}*/
  paramVals?: Record<string, string>;
  /** 是否显示 */
  visible?: boolean;
  /** 宽度 */
  width?: number;
  /** 是否通过dao保存 */
  saveByDao?: boolean;
  /** 是否禁用数据转换 */
  forbidConvert?: boolean;
  /** 是否显示改变后的回调 */
  onVisibleChange: (visible: boolean) => void;
};
type StateField = {
  step?: number;
  uploadResult?: API.UploadResult;
  importProgress?: API.ImportProgress;
  paramVals?: Record<string, any>;
};
const DataImport: React.FC<PropField> = (props) => {
  const { curPermID } = useModel('sysInfo');
  const [state, dispatch] = useReducer(
    (prevState: StateField, newState: StateField) => {
      return { ...prevState, ...newState };
    },
    {
      step: 1,
      uploadResult: undefined,
      importProgress: undefined,
      paramVals: props.paramVals,
    },
  );
  const formRef1 = useRef<ProFormInstance<any>>();
  const formRef2 = useRef<ProFormInstance<any>>();
  const importData = useCallback(
    async (pVals: Record<string, any>, dataMap: API.DataMapItem[], isNewMap: boolean) => {
      const importInfo = {
        ImportName: props.importName,
        DaoName: props.daoName,
        FilePath: state.uploadResult!.fileName,
        SheetName: state.uploadResult!.sheetNames[0],
        Mode: props.importMode,
        Params: pVals,
        Keys: props.keys!,
        HasColHeader: true,
        SaveByDao: props.saveByDao!,
        ForbidConvert: props.forbidConvert!,
        PermId: curPermID,
      };
      dataImport(importInfo, '', [], dataMap, isNewMap)
        .then(function () {
          dispatch({ step: 3 });
        })
        .catch(function (ex) {
          console.error('导入', props.importName, '错误', ex);
        });
    },
    [
      curPermID,
      props.daoName,
      props.forbidConvert,
      props.importMode,
      props.importName,
      props.keys,
      props.saveByDao,
      state.uploadResult,
    ],
  );
  const form1Submit = useCallback(
    async (vals: Record<string, any>) => {
      Object.keys(vals).forEach((key) => {
        if (vals[key] instanceof Date) {
          vals[key] = format(vals[key], 'yyyy-MM-dd');
        } else if (typeof vals[key] === 'number') {
          vals[key] = '' + vals[key];
        }
      });
      const pVals = { ...props.paramVals, ...vals };
      if (_.isEmpty(state.uploadResult?.dataMap)) {
        dispatch({ step: 2, paramVals: pVals });
        return;
      } else {
        dispatch({ paramVals: pVals });
        importData(pVals, state.uploadResult!.dataMap, false);
      }
    },
    [importData, state.uploadResult, props.paramVals],
  );
  const form2Submit = useCallback(
    async (vals: Record<string, any>) => {
      const dataMaps: API.DataMapItem[] = [];
      props.controls.forEach((c) => {
        let convert: API.DataConvert | undefined = undefined;
        if (!_.isEmpty(c.options)) {
          const ids: string[] = [];
          const texts: string[] = [];
          const opts = c.options! as API.OptionItem[];
          opts?.forEach((d) => {
            ids.push(String(d.value));
            texts.push(d.label);
          });
          convert = {
            DaoName: '#fixed',
            IdColName: ids.join(':'),
            TextColName: texts.join(':'),
            Multiple: false,
            IsDao: false,
          };
        } else if (c.fetch) {
          convert = {
            DaoName: c.fetch.tableName,
            IdColName: c.fetch.valueTpl,
            TextColName: c.fetch.optionTpl,
            Multiple: c.multiple!,
            IsDao: c.fetch.isDao!,
          };
        }
        const idxStr = vals[c.name] as string;
        const idx = idxStr.startsWith('@') ? -1 : Number(vals[c.name]);
        const colName = idx === -1 ? idxStr : state.uploadResult!.columnHeaders[idx];
        const dMap = {
          DestColName: c.name,
          IsRequest:
            !_.isEmpty(c.rules) && c.rules?.findIndex((r) => r.required === true) !== -1
              ? true
              : false,
          Convert: convert!,
          SrcColIndex: idx,
          SrcColName: colName,
        };
        dataMaps.push(dMap);
      });
      importData(state.paramVals!, dataMaps, true);
    },
    [importData, state.paramVals, props.controls, state.uploadResult],
  );
  const next = useCallback(() => {
    if (_.isEmpty(state.uploadResult?.fileName)) {
      message.info('请选择要的导入的excel文件');
      return;
    }
    if (state.step === 1) {
      if (!_.isEmpty(props.paramNames)) {
        if (_.isEmpty(formRef1.current)) {
          return;
        }
        formRef1.current?.submit();
      } else {
        if (_.isEmpty(state.uploadResult?.dataMap)) {
          dispatch({ step: 2 });
        } else {
          importData(state.paramVals!, state.uploadResult!.dataMap, false);
        }
      }
    } else {
      formRef2.current?.submit();
    }
  }, [importData, state.paramVals, state.step, state.uploadResult, props.paramNames]);
  const uploadFC = useMemo(() => {
    if (state.step !== 1) {
      return null;
    }
    const beforeUpload = (file: UploadFile<any>) => {
      const isXlsx = file.name.lastIndexOf('.xlsx') !== -1;
      if (!isXlsx) {
        message.error('系统仅支持xlsx文件格式，请转换后再导入!');
        return false;
      }
      const isLt100M = file.size! / 1024 / 1024 < 100;
      if (!isLt100M) {
        message.error('文件不能大于100MB!');
        return false;
      }
      return true;
    };
    const handleUploadChange = (info: UploadChangeParam<UploadFile<any>>) => {
      if (info.file.status === 'done') {
        const res = info.file.response as API.UploadResult;
        dispatch({ uploadResult: res });
        message.success(`${info.file.name} 文件上传成功！`);
      } else if (info.file.status === 'error') {
        const msg = JSON.parse(info.file.response.message);
        message.error(`文件${info.file.name}${msg.message}`, 5);
      }
    };
    const upLoadProps = {
      name: props.importName,
      action: `/api/Upload/${props.importName}`,
      onChange: handleUploadChange,
      beforeUpload: beforeUpload,
      accept: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      multiple: false,
    };
    const controls: API.Control[] = [];
    if (!_.isEmpty(props.paramNames)) {
      props.paramNames?.forEach((p) => {
        if (!props.paramVals![p]) {
          for (let i = 0; i < props.controls.length; i++) {
            if (props.controls[i].name === p) {
              const cont = { ...props.controls[i] };
              cont.grid = 24;
              controls.push(cont);
            }
          }
        }
      });
    }
    if (
      _.isEmpty(props.keys) &&
      (props.importMode === 'InsertAndUpdate' || props.importMode === 'UpdateOnly')
    ) {
      message.error('对于导入方式InsertAndUpdate和UpdateOnly必须指定确定记录唯一性的keys');
      return null;
    } else if (_.isEmpty(props.paramNames) && props.importMode === 'DeleteAfterInsert') {
      message.error('对于导入方式DeleteAfterInsert必须指定paramNames,或者请使用TruncteAfterInsert');
      return null;
    }
    return (
      <>
        {!_.isEmpty(props.paramNames) ? (
          <DataEdit
            formRef={formRef1}
            controls={controls}
            initVals={props.paramVals}
            showType="inner"
            layout="horizontal"
            onSumit={form1Submit}
            renderButtons={false}
          />
        ) : null}
        <Upload.Dragger {...upLoadProps}>
          <p className="ant-upload-drag-icon">
            <InboxOutlined />
          </p>
          <p className="ant-upload-text">点击选择或者拖拽一个excel文件到这里</p>
          <p className="ant-upload-hint">注意excel必须是xlsx格式，数据要放在第一个sheet中。</p>
        </Upload.Dragger>
      </>
    );
  }, [
    props.controls,
    props.paramNames,
    props.paramVals,
    props.importName,
    state.step,
    form1Submit,
    props.importMode,
    props.keys,
  ]);
  const fieldMapFC = useMemo(() => {
    if (state.step !== 2) {
      return null;
    }
    let grid = 24;
    if (props.controls.length >= 7 && props.controls.length <= 16) {
      grid = 12;
    } else if (props.controls.length > 16) {
      grid = 8;
    }
    const items: API.Control[] = [];
    const initVals: Record<string, any> = {};
    props.controls.forEach((c) => {
      const isParams = props.paramNames?.includes(c.name);
      const datas: API.OptionItem[] = [];
      if (isParams) {
        datas.push({ value: '@' + c.name, label: c.label + '参数' });
        initVals[c.name] = '@' + c.name;
      } else {
        state.uploadResult!.columnHeaders.forEach((ch, i) => {
          datas.push({ value: String(i), label: ch });
          if (ch === c.label) {
            initVals[c.name] = String(i);
          }
        });
      }
      items.push({
        type: 'select',
        grid: grid,
        options: datas,
        name: c.name,
        label: c.label,
        rules: [{ required: true, message: `${c.label}不能为空` }],
      });
    });
    return (
      <DataEdit
        formRef={formRef2}
        controls={items}
        showType="inner"
        layout="vertical"
        initVals={initVals}
        autoFocusFirstInput={false}
        onSumit={form2Submit}
        renderButtons={false}
      />
    );
  }, [props.controls, props.paramNames, state.uploadResult, state.step, form2Submit]);
  const processFC = useMemo(() => {
    if (state.step !== 3) {
      return null;
    }
    return (
      <div style={{ textAlign: 'center', verticalAlign: 'middle' }}>
        <Progress
          type="circle"
          percent={state.importProgress?.percent}
          format={(percent) => `${percent} %`}
        />
      </div>
    );
  }, [state.importProgress, state.step]);
  const resultFC = useMemo(() => {
    if (state.step !== 4) {
      return null;
    }
    let retComp: JSX.Element | undefined = undefined;
    if (state.importProgress?.success === '1') {
      retComp = (
        <div key="1">
          数据导入完成，成功
          <Badge
            count={state.importProgress.successCount}
            overflowCount={1000000}
            style={{ backgroundColor: '#87d068' }}
          />
          行
        </div>
      );
    } else {
      const errInfos = state.importProgress?.err.split('|');
      retComp = (
        <div key="2" style={{ color: 'red' }}>
          <p key="1">数据校验失败</p>
          {errInfos!.map((err, i) => (
            <p key={i as React.Key}>{err}</p>
          ))}
        </div>
      );
    }
    return retComp;
  }, [state.importProgress, state.step]);
  const closeModal = useCallback(() => {
    if (props.onVisibleChange) {
      props.onVisibleChange(false);
    }
    if (!_.isEmpty(state.uploadResult?.fileName)) {
      deleteUploadFile(state.uploadResult!.fileName);
    }
    dispatch({ uploadResult: undefined, step: 1 });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [state.uploadResult]);
  useEffect(() => {
    if (state.step! < 3 || _.isEmpty(state.uploadResult?.fileName)) {
      return;
    }
    const getProgress = () => {
      dataProgress(state.uploadResult!.fileName)
        .then(function (data) {
          if (data.finish === '1') {
            dispatch({ importProgress: data, step: 4 });
          } else {
            dispatch({ importProgress: data, step: 3 });
          }
        })
        .catch(function (ex) {
          console.error('获取', props.importName, '的导入进度错误', ex);
        });
    };
    if (state.step === 3 && timer === 0) {
      timer = window.setInterval(getProgress, 1000);
    }
    if (state.step === 4) {
      timer = 0;
      clearTimeout(timer);
    }
    return () => {
      clearTimeout(timer);
    };
  }, [state.step, props.importName, state.uploadResult]);

  let titleSuffix = '选择导入文件';
  if (state.step === 2) {
    titleSuffix = '设置映射关系';
  } else if (state.step === 3) {
    titleSuffix = '正在导入';
  } else if (state.step === 4) {
    titleSuffix = '导入结果';
  }
  return (
    <Modal
      title={
        <div>
          {props.title}
          <span style={{ float: 'right', marginRight: 30 }}>{titleSuffix}</span>
        </div>
      }
      maskClosable={false}
      width={state.step === 2 && props.controls.length >= 7 ? props.width! + 300 : props.width!}
      visible={props.visible}
      onOk={() => next()}
      onCancel={() => closeModal()}
      footer={[
        <Button key="import" type="primary" disabled={state.step! >= 3} onClick={() => next()}>
          {_.isEmpty(state.uploadResult?.dataMap) ? '下一步' : '导入'}
        </Button>,
        <Button key="close" type="ghost" disabled={state.step === 3} onClick={() => closeModal()}>
          关闭
        </Button>,
      ]}
    >
      {uploadFC}
      {fieldMapFC}
      {processFC}
      {resultFC}
    </Modal>
  );
};
export default DataImport;
DataImport.defaultProps = {
  width: 500,
  visible: false,
  saveByDao: true,
  forbidConvert: true,
  paramVals: {},
};
